
#pragma once

#include "config.hpp"

#include "atr/list.hpp"

template <typename T>
class ModifiersTest : public ::testing::Test 
{};

TYPED_TEST_CASE_P(ModifiersTest);

TYPED_TEST_P(ModifiersTest, PopFront_EmptyList)
{
    using T1 = TYPE_PARAM<int>::pop_front;
    using T2 = TYPE_PARAM<int, long>::pop_front::pop_front;

    using E1 = TYPE_PARAM<>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E1, T2 );
}

TYPED_TEST_P(ModifiersTest, PopFront)
{
    using T1 = list<int, char>::pop_front;
    using T2 = list<long, int, char>::pop_front;
    using T3 = list<size_t, int, long, char>::pop_front::pop_front;

    using E1 = list<char>;
    using E2 = list<int, char>;
    using E3 = list<long, char>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
    EXPECT_SAME( E3, T3 );
}

TYPED_TEST_P(ModifiersTest, PushFront_EmptyList)
{
    using T1 = TYPE_PARAM<>::template push_front<>;
    using T2 = TYPE_PARAM<>::template push_front<int>;
    using T3 = TYPE_PARAM<>::template push_front<int, long, char>;

    using E1 = TYPE_PARAM<>;
    using E2 = TYPE_PARAM<int>;
    using E3 = TYPE_PARAM<int, long, char>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
    EXPECT_SAME( E3, T3 );
}

TYPED_TEST_P(ModifiersTest, PushFront)
{
    using T1 = TYPE_PARAM<size_t>::template push_front<>;
    using T2 = TYPE_PARAM<char>::template push_front<int>;
    using T3 = TYPE_PARAM<long, int>::template push_front<int, long, char>;

    using E1 = TYPE_PARAM<size_t>;
    using E2 = TYPE_PARAM<int, char>;
    using E3 = TYPE_PARAM<int, long, char, long, int>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
    EXPECT_SAME( E3, T3 );
}

TYPED_TEST_P(ModifiersTest, PopBack_EmptyList)
{
    using T1 = TYPE_PARAM<int>::pop_back;
    using T2 = TYPE_PARAM<int, long>::pop_back::pop_back;

    using E1 = TYPE_PARAM<>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E1, T2 );
}

TYPED_TEST_P(ModifiersTest, PopBack)
{
    using T1 = TYPE_PARAM<int>::pop_back;
    using T2 = TYPE_PARAM<int, char>::pop_back;
    using T3 = TYPE_PARAM<long, int, char>::pop_back;
    using T4 = TYPE_PARAM<size_t, int, long, char>::pop_back::pop_back;

    using E1 = TYPE_PARAM<>;
    using E2 = TYPE_PARAM<int>;
    using E3 = TYPE_PARAM<long, int>;
    using E4 = TYPE_PARAM<size_t, int>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
    EXPECT_SAME( E3, T3 );
    EXPECT_SAME( E4, T4 );
}

TYPED_TEST_P(ModifiersTest, PushBack_EmptyList)
{
    using T1 = TYPE_PARAM<>::template push_back<>;
    using T2 = TYPE_PARAM<>::template push_back<int>;
    using T3 = TYPE_PARAM<>::template push_back<int, long, char>;

    using E1 = TYPE_PARAM<>;
    using E2 = TYPE_PARAM<int>;
    using E3 = TYPE_PARAM<int, long, char>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
    EXPECT_SAME( E3, T3 );
}

TYPED_TEST_P(ModifiersTest, PushBack)
{
    using T1 = TYPE_PARAM<size_t>::template push_back<>;
    using T2 = TYPE_PARAM<char>::template push_back<int>;
    using T3 = TYPE_PARAM<long, int>::template push_back<int, long, char>;

    using E1 = TYPE_PARAM<size_t>;
    using E2 = TYPE_PARAM<char, int>;
    using E3 = TYPE_PARAM<long, int, int, long, char>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
    EXPECT_SAME( E3, T3 );
}

TYPED_TEST_P(ModifiersTest, Swap_EmptyList)
{
    using E1 = TYPE_PARAM<>;
    using E2 = TYPE_PARAM<int>;
    using E3 = TYPE_PARAM<int, long, char>;

    using T1 = TYPE_PARAM<>::template swap<E1>;
    using T2 = TYPE_PARAM<>::template swap<E2>;
    using T3 = TYPE_PARAM<>::template swap<E3>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
    EXPECT_SAME( E3, T3 );
}

TYPED_TEST_P(ModifiersTest, Swap)
{
    using E1 = TYPE_PARAM<>;
    using E2 = TYPE_PARAM<int>;
    using E3 = TYPE_PARAM<int, long, char>;

    using T1 = TYPE_PARAM<char, long>::template swap<E1>;
    using T2 = TYPE_PARAM<int, size_t>::template swap<E2>;
    using T3 = TYPE_PARAM<long, int, char>::template swap<E3>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
    EXPECT_SAME( E3, T3 );
}

TYPED_TEST_P(ModifiersTest, Clear)
{
    using T1 = TYPE_PARAM<>::clear;
    using T2 = TYPE_PARAM<int>::clear;
    using T3 = TYPE_PARAM<int, char, long>::clear;

    EXPECT_SAME( TYPE_PARAM<>, T1 );
    EXPECT_SAME( TYPE_PARAM<>, T2 );
    EXPECT_SAME( TYPE_PARAM<>, T3 );
}

TYPED_TEST_P(ModifiersTest, Insert_Value_SingleElement_EmptyList)
{
    using T1 = TYPE_PARAM<>::template insert<TYPE_PARAM<>::begin, int>;
    using T2 = TYPE_PARAM<>::template insert<TYPE_PARAM<>::end, char, int>;

    using E1 = TYPE_PARAM<int>;
    using E2 = TYPE_PARAM<char, int>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
}

TYPED_TEST_P(ModifiersTest, Insert_Value_SingleElement)
{
    using T1 = TYPE_PARAM<int>::template insert<TYPE_PARAM<int>::begin, int>;
    using T2 = TYPE_PARAM<long, char>::
                        template insert<TYPE_PARAM<long, char>::end, char, int>;
    using T3 = typename T2::template insert<typename T2::begin::next, size_t>;

    using E1 = TYPE_PARAM<int, int>;
    using E2 = TYPE_PARAM<long, char, char, int>;
    using E3 = TYPE_PARAM<long, size_t, char, char, int>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
    EXPECT_SAME( E3, T3 );
}

TYPED_TEST_P(ModifiersTest, Insert_Value_EmptyList)
{
    using T1 = TYPE_PARAM<>
                ::template insert<TYPE_PARAM<>::begin, int, long>;
    using T2 = TYPE_PARAM<>
                ::template insert<TYPE_PARAM<>::end, char, int, size_t>;

    using E1 = TYPE_PARAM<int, long>;
    using E2 = TYPE_PARAM<char, int, size_t>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
}

TYPED_TEST_P(ModifiersTest, Insert_Value)
{
    using T1 = TYPE_PARAM<int>
                ::template insert<TYPE_PARAM<int>::begin, int, long>;
    using T2 = TYPE_PARAM<long, char>
                ::template insert<TYPE_PARAM<long, char>::end, char, int, size_t>;
    using T3 = typename T2
                ::template insert<typename T2::begin::next, size_t, int>;

    using E1 = TYPE_PARAM<int, long, int>;
    using E2 = TYPE_PARAM<long, char, char, int, size_t>;
    using E3 = TYPE_PARAM<long, size_t, int, char, char, int, size_t>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
    EXPECT_SAME( E3, T3 );
}

TYPED_TEST_P(ModifiersTest, Insert_FirstLast_EmptyList)
{
    using T = TYPE_PARAM<>;
    using I = TYPE_PARAM<int, long, size_t, char>;

    using T1 = typename T::template insert<typename T::begin, 
                                typename I::begin, typename I::end>;
    using T2 = typename T::template insert<typename T::begin, 
                               typename I::begin::next, typename I::end>;
    using T3 = typename T::template insert<typename T::end,   
                                typename I::begin, typename I::end::prev>;
    using T4 = typename T::template insert<typename T::end,   
                                typename I::begin::next, typename I::end::prev>;

    using E1 = TYPE_PARAM<int, long, size_t, char>;
    using E2 = TYPE_PARAM<long, size_t, char>;
    using E3 = TYPE_PARAM<int, long, size_t>;
    using E4 = TYPE_PARAM<long, size_t>;
    
    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
    EXPECT_SAME( E3, T3 );
    EXPECT_SAME( E4, T4 );
}

TYPED_TEST_P(ModifiersTest, Insert_FirstLast)
{
    using T = TYPE_PARAM<wchar_t, int64_t, bool>;
    using I = TYPE_PARAM<int, long, size_t, char>;

    using T1 = typename T::template insert<typename T::begin, 
                                typename I::begin, typename I::end>;
    using T2 = typename T::template insert<typename T::begin::next, 
                               typename I::begin::next, typename I::end>;
    using T3 = typename T::template insert<typename T::end::prev,   
                                typename I::begin, typename I::end::prev>;
    using T4 = typename T::template insert<typename T::end,   
                                typename I::begin::next, typename I::end::prev>;

    using E1 = TYPE_PARAM<int, long, size_t, char, wchar_t, int64_t, bool>;
    using E2 = TYPE_PARAM<wchar_t, long, size_t, char, int64_t, bool>;
    using E3 = TYPE_PARAM<wchar_t, int64_t, int, long, size_t, bool>;
    using E4 = TYPE_PARAM<wchar_t, int64_t, bool, long, size_t>;
    
    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
    EXPECT_SAME( E3, T3 );
    EXPECT_SAME( E4, T4 );
}

TYPED_TEST_P(ModifiersTest, Erase_SingleElement_Simple)
{
    using T = TYPE_PARAM<int, char, long, int>;

    using T1 = typename T::template erase<typename T::begin>;
    using T2 = typename T::template erase<typename T::end>;
    using T3 = typename T::template erase<typename T::begin::next>;
    using T4 = typename T::template erase<typename T::end::prev>;

    using E1 = TYPE_PARAM<char, long, int>;
    using E2 = TYPE_PARAM<int, char, long, int>;
    using E3 = TYPE_PARAM<int, long, int>;
    using E4 = TYPE_PARAM<int, char, long>;

    EXPECT_SAME( E1, T1 );
    EXPECT_SAME( E2, T2 );
    EXPECT_SAME( E3, T3 );
    EXPECT_SAME( E4, T4 );
}

REGISTER_TYPED_TEST_CASE_P(ModifiersTest, PopFront_EmptyList, PopFront,
                                          PushFront_EmptyList, PushFront,
                                          PopBack_EmptyList, PopBack,
                                          PushBack_EmptyList, PushBack,
                                          Swap_EmptyList, Swap,
                                          Clear,
                                          Insert_Value_SingleElement_EmptyList,
                                          Insert_Value_SingleElement,
                                          Insert_Value_EmptyList,
                                          Insert_Value,
                                          Insert_FirstLast_EmptyList,
                                          Insert_FirstLast,
                                          Erase_SingleElement_Simple);
